package top.beiermode.geektime.context;

import top.beiermode.geektime.function.ThrowableAction;
import top.beiermode.geektime.function.ThrowableFunction;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import javax.naming.*;
import javax.servlet.ServletContext;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Java 传统组件上下文（基于 JNDI 实现）
 *
 * @author Chen Song
 * @since 1.0
 */
public class ClassicComponentContext implements ComponentContext {

    public static final String CONTEXT_NAME = ComponentContext.class.getName();

    private static final String COMPONENT_ENV_CONTEXT_NAME = "java:comp/env";

    private static final Logger logger = Logger.getLogger(CONTEXT_NAME);

    private static ServletContext servletContext;

    private Context envContext;

    private ClassLoader classLoader;

    private Map<String, Object> componentCache = new LinkedHashMap<>();

    /**
     * {@link PreDestroy} 方法缓存，Key 为标注的方法，Value 为方法所属的对象
     */
    private Map<Method, Object> preDestroyMethodCache = new LinkedHashMap<>();

    public void init(ServletContext servletContext) throws RuntimeException {
        ClassicComponentContext.servletContext = servletContext;
        servletContext.setAttribute(CONTEXT_NAME, this);
        this.init();
    }

    /**
     * 获取 ComponentContext
     *
     * @return
     */
    public static ClassicComponentContext getInstance() {
        return (ClassicComponentContext) servletContext.getAttribute(CONTEXT_NAME);
    }

    /**
     * 实例化组件
     */
    protected void instantiateComponents() {
        List<String> componentNames = listAllComponentNames();
        componentNames.forEach(name -> componentCache.put(name, lookupComponent(name)));
    }

    /**
     * 初始化组件（支持 Java 标准 Commons Annotation 生命周期）
     */
    protected void initializeComponents() {
        componentCache.values().forEach(this::initializeComponent);
    }

    /**
     * 初始化组件（支持 Java 标准 Commons Annotation 生命周期）
     * <ol>
     *   <li>注入阶段 - {@link Resource}<li/>
     *   <li>初始阶段 - {@link PostConstruct}<li/>
     *   <li>销毁阶段 - {@link PreDestroy}<li/>
     * </ol>
     */
    public void initializeComponent(Object component) {
        Class<?> componentClass = component.getClass();
        // 注入阶段 @Resource
        injectComponent(component, componentClass);
        // 查询候选方法
        List<Method> candidateMethod = findCandidateMethods(componentClass);
        // 初始阶段 @PostConstruct
        processPostConstruct(component, candidateMethod);
        // 保存 @PreDestroy 的元信息
        processPreDestroyMetadata(component, candidateMethod);
    }

    /**
     * 字段注入
     */
    protected void injectComponent(Object component, Class<?> componentClass) {
        Stream.of(componentClass.getDeclaredFields())
                .filter(field ->
                        !Modifier.isStatic(field.getModifiers()) &&
                                field.isAnnotationPresent(Resource.class))
                .forEach(field -> {
                    Resource resource = field.getAnnotation(Resource.class);
                    String resourceName = resource.name();
                    // 没有解决循环依赖
                    Object injectedObject = lookupComponent(resourceName);
                    field.setAccessible(true);
                    try {
                        field.set(component, injectedObject);
                    } catch (IllegalAccessException e) {
                    }
                });
    }

    /**
     * 获取组件类中的候选方法（public、非 static、无参数）
     */
    protected List<Method> findCandidateMethods(Class<?> componentClass) {
        return Stream.of(componentClass.getMethods())
                .filter(method ->
                        !Modifier.isStatic(method.getModifiers()) &&
                                method.getParameterCount() == 0)
                .collect(Collectors.toList());
    }

    /**
     * 执行组件初始化方法
     */
    protected void processPostConstruct(Object component, List<Method> componentClass) {
        componentClass.stream()
                .filter(method -> method.isAnnotationPresent(PostConstruct.class))
                .forEach(method -> {
                    ThrowableAction.execute(() -> method.invoke(component));
                });
    }

    /**
     * 保存销毁阶段元信息
     */
    protected void processPreDestroyMetadata(Object component, List<Method> candidateMethod) {
        candidateMethod.stream()
                .filter(method -> method.isAnnotationPresent(PreDestroy.class))
                .forEach(method -> {
                    preDestroyMethodCache.put(method, component);
                });
    }



    protected List<String> listAllComponentNames() {
        return listAllComponentNames("/");
    }

    protected List<String> listAllComponentNames(String name) {
        return executeInContext(context -> {
            NamingEnumeration<NameClassPair> e = executeInContext(context, ctx -> ctx.list(name), true);
            // 当前 JNDI 名称下没有子节点
            if (e == null) {
                return Collections.emptyList();
            }

            List<String> fullNames = new LinkedList<>();
            while (e.hasMoreElements()) {
                NameClassPair element = e.nextElement();
                String className = element.getClassName();
                Class<?> targetClass = classLoader.loadClass(className);
                if (Context.class.isAssignableFrom(targetClass)) {
                    // 当前是目录，递归查找
                    fullNames.addAll(listAllComponentNames(element.getName()));
                } else {
                    String fullName = name.startsWith("/")
                            ? element.getName() : name + "/" + element.getName();
                    fullNames.add(fullName);
                }
            }
            return fullNames;
        } );
    }

    /**
     *
     */
    public <C> C lookupComponent(String name) {
        return executeInContext(context -> (C) context.lookup(name));
    }

    /**
     * 在 Context 中执行，通过指定 ThrowableFunction 返回结果
     *
     * @param function ThrowableFunction
     * @param <R>      返回结果类型
     * @return R
     * @see ThrowableFunction#apply(Object)
     */
    protected <R> R executeInContext(ThrowableFunction<Context, R> function) {
        return executeInContext(function, false);
    }

    /**
     * 在 Context 中执行，通过指定 ThrowableFunction 返回结果
     *
     * @param function         ThrowableFunction
     * @param ignoredException 是否忽略异常
     * @param <R>              返回结果类型
     * @return R
     * @see ThrowableFunction#apply(Object)
     */
    protected <R> R executeInContext(ThrowableFunction<Context,R> function, boolean ignoredException) {
        return executeInContext(this.envContext, function, ignoredException);
    }

    /**
     * 在 Context 中执行，通过指定 ThrowableFunction 返回结果
     *
     * @param context       Context
     * @param function         ThrowableFunction
     * @param ignoredException 是否忽略异常
     * @param <R>              返回结果类型
     * @return R
     * @see ThrowableFunction#apply(Object)
     */
    protected <R> R executeInContext(Context context, ThrowableFunction<Context,R> function, boolean ignoredException) {
        R result = null;
        try {
            result = ThrowableFunction.execute(context, function);
        } catch (Throwable e) {
            if (ignoredException) {
                logger.warning(e.getMessage());
            } else {
                throw new RuntimeException(e);
            }
        }
        return result;
    }

    @Override
    public void init() {
        initClassLoader();
        initEnvContext();
        instantiateComponents();
        initializeComponents();
        registerShutdownHook();
    }

    @Override
    public void destroy() {
        processPreDestroy();
        clearCache();
        closeEnvContext();
    }

    @Override
    public <C> C getComponent(String name) {
        return (C) componentCache.get(name);
    }

    @Override
    public List<String> getComponentNames() {
        return new ArrayList<>(componentCache.keySet());
    }

    private void initClassLoader() {
        // 获取当前 ServletContext 的 ClassLoader
        this.classLoader = servletContext.getClassLoader();
    }

    private void initEnvContext() throws RuntimeException {
        if (this.envContext != null) {
            return;
        }
        Context context = null;
        try {
            context = new InitialContext();
            this.envContext = (Context) context.lookup(COMPONENT_ENV_CONTEXT_NAME);
        } catch (NamingException e) {
            throw new RuntimeException(e);
        } finally {
            close(context);
        }
    }

    private void clearCache() {
        componentCache.clear();
        preDestroyMethodCache.clear();
    }

    private void closeEnvContext() {
        close(this.envContext);
    }

    /**
     * 注册 ShutdownHook
     */
    private void registerShutdownHook() {
        Runtime.getRuntime().addShutdownHook(new Thread(this::processPreDestroy));
    }

    /**
     * 执行组件销毁方法
     */
    private void processPreDestroy() {
        for (Method preDestroyMethod : preDestroyMethodCache.keySet()) {
            Object component = preDestroyMethodCache.remove(preDestroyMethod);
            ThrowableAction.execute(() -> preDestroyMethod.invoke(component));
        }
    }

    private static void close(Context context) {
        if (context != null) {
            ThrowableAction.execute(context::close);
        }
    }
}
